<?php

namespace app\common\lang;

use think\App;
use think\Config;
use think\facade\Cache;

class Lang extends \think\Lang
{
    /**
     * 多语言信息
     * @var array
     */
    private array $lang = [];

    /**
     * 当前语言
     * @var string
     */
    private string $range = 'zh-cn';

    /**
     * 构造方法
     * @access public
     * @param array $config
     */
    public function __construct(App $app, array $config = [])
    {
        parent::__construct($app, $config);
        $this->range  = $this->config['default_lang'];
    }

    public static function __make(App $app, Config $config)
    {
        return new static($app, $config->get('lang'));
    }


    /**
     * 设置当前语言
     * @access public
     * @param string $lang 语言
     * @return void
     */
    public function setLangSet(string $lang): void
    {
        $this->range = $lang;
    }

    /**
     * 获取当前语言
     * @access public
     * @return string
     */
    public function getLangSet(): string
    {
        return $this->range;
    }

    /**
     * 切换语言
     * @access public
     * @param string $langset 语言
     * @return void
     */
    public function switchLangSet(string $langset)
    {
        if (empty($langset)) {
            return;
        }

        $this->setLangSet($langset);

        // 加载系统语言包
        $this->load([
            $this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php',
        ]);

        // 加载应用语言包（支持多种类型）
        $files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
        $this->load($files);

        // 加载扩展（自定义）语言包
        $list = $this->app->config->get('lang.extend_list', []);

        if (isset($list[$langset])) {
            $this->load($list[$langset]);
        }
    }

    /**
     * 重写load方法
     *
     * 替换原本的加载语言内容
     * 加载语言定义(不区分大小写)
     * @access public
     * @param string|array $file 语言文件
     * @param string $range 语言作用域
     * @return array
     */
    public function load($file, $range = ''): array
    {
        $range = $range ?: $this->range;
        if (!isset($this->lang[$range])) {
            $this->lang[$range] = [];
        }

        $lang = [];

        foreach ((array) $file as $name) {
            if (is_file($name)) {
                $result = $this->parse($name);
                $lang   = array_change_key_case($result) + $lang;
            }
        }

        if (!empty($lang)) {
            $this->lang[$range] = $lang + $this->lang[$range];
        }

        // 优先从缓存加载语言数据
        $cache = Cache::get('lang');
        $cacheData = $cache[$range] ?? [];
        $fileData = parent::load($file, $range);

        if (!isset($this->lang[$range])) {
            $this->lang[$range] = $fileData;
        }

        if ($cacheData) {
            $this->lang[$range] = $this->lang[$range] + $cacheData;
        }
        return $this->lang[$range];
    }

    /**
     * 解析语言文件
     * @access protected
     * @param string $file 语言文件名
     * @return array
     */
    protected function parse(string $file): array
    {
        $type   = pathinfo($file, PATHINFO_EXTENSION);
        $result = match ($type) {
            'php'       =>  include $file,
            'yml','yaml'=>  function_exists('yaml_parse_file') ? yaml_parse_file($file) : [],
            'json'      =>  json_decode(file_get_contents($file), true),
            default     =>  [],
        };

        return is_array($result) ? $result : [];
    }

    /**
     * 判断是否存在语言定义(不区分大小写)
     * @access public
     * @param string  $name  语言变量
     * @param string  $range 语言作用域
     * @return bool
     */
    public function has(string $name, string $range = ''): bool
    {
        $range = $range ?: $this->range;

        if ($this->config['allow_group'] && str_contains($name, '.')) {
            [$name1, $name2] = explode('.', $name, 2);
            return isset($this->lang[$range][strtolower($name1)][$name2]);
        }

        return isset($this->lang[$range][strtolower($name)]);
    }
    /**
     * 获取语言定义(不区分大小写)
     * @access public
     * @param string|null $name 语言变量
     * @param array $vars 变量替换
     * @param string $range 语言作用域
     * @return mixed
     */
    public function get(?string $name = null, array $vars = [], string $range = '')
    {
        $range = $range ?: $this->range;

        if (!isset($this->lang[$range])) {
            $this->switchLangSet($range);
        }

        // 空参数返回所有定义
        if (is_null($name)) {
            return $this->lang[$range] ?? [];
        }

        if ($this->config['allow_group'] && str_contains($name, '.')) {
            [$name1, $name2] = explode('.', $name, 2);

            $value = $this->lang[$range][strtolower($name1)][$name2] ?? $name;
        } else {
            $value = $this->lang[$range][strtolower($name)] ?? $name;
        }

        // 变量解析
        if (!empty($vars) && is_array($vars)) {
            /**
             * Notes:
             * 为了检测的方便，数字索引的判断仅仅是参数数组的第一个元素的key为数字0
             * 数字索引采用的是系统的 sprintf 函数替换，用法请参考 sprintf 函数
             */
            if (key($vars) === 0) {
                // 数字索引解析
                array_unshift($vars, $value);
                $value = call_user_func_array('sprintf', $vars);
            } else {
                // 关联索引解析
                $replace = array_keys($vars);
                foreach ($replace as &$v) {
                    $v = "{:{$v}}";
                }
                $value = str_replace($replace, $vars, $value);
            }
        }

        return $value;
    }
}